package com.xike.net;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.ByteArrayBuffer;
import org.apache.http.util.CharArrayBuffer;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.xike.util.MLog;
import com.xike.util.Manager;
import com.xike.util.StringUtil;

public abstract class HttpClient {
	public final static int NET_SUCCESS = HttpURLConnection.HTTP_OK;
	public final static int NET_IO_ERROR = 1;
	public final static int NET_URL_ERROR = 2;
	public final static int NET_ENCODING_ERROR = 3;
	public final static int NET_CLIENT_PROTOCOL_ERROR = 4;
	public final static int NET_SAXCONFIG_ERROR = 5;
	public final static int NET_SAXPARSER_ERROR = 6;
	public final static int NET_UNKONWN_ERROR = 99;
	public final static int SAX_OK = 100;
	public final static int SAX_ERROR = 101;

	public final static int HTTP_METHOD_GET = 0;
	public final static int HTTP_METHOD_POST = 1;

	protected BasicHeader[] defaultHeaders = null;
	protected ArrayList<BasicNameValuePair> postData = new ArrayList<BasicNameValuePair>();
	// protected BasicNameValuePair[] postData = null;

	protected boolean useDefaultHeader = false;
	protected String contentEncoding = NetConstant.DEFAULT_CONTENT_CHARSET;
	protected long contentLength = 0;
	protected URL url;
	protected InputStream is;
	protected String urlMd5;

	protected boolean useCache = false;

	public HttpClient() {
	}

	protected abstract void init();

	protected abstract void init(String cacheFilePath);

	protected abstract void close();

	protected abstract int doMethod(int method);

	/**
	 * @param urlMain
	 *            请求url域名地址
	 * @param useCache
	 *            是否使用文件缓存
	 * @param queryData
	 *            请求参数
	 * @return
	 * @WZL wzl200711402@163.com @2012-7-27@上午10:53:56
	 * @description HttpGet 带参数请求
	 */
	public int methodGet(String urlMain, boolean useCache, BasicNameValuePair... queryData) {
		// URL ENCODER问题，queryData的转义处理
		if (queryData != null) {
			StringBuffer sb = new StringBuffer(urlMain);
			if (urlMain.indexOf('?') != -1) {
				sb.append('&');
			} else {
				sb.append('?');
			}
			for (BasicNameValuePair nv : queryData) {
				if (nv != null) {
					sb.append(nv.getName()).append('=').append(nv.getValue()).append('&');
				}
			}
			return methodGet(sb.toString(), useCache);
		}
		return methodGet(urlMain, useCache);
	}

	public int methodGet(String urlMain, boolean useCache, String appSerret, BasicNameValuePair... queryData) {
		// URL ENCODER问题，queryData的转义处理
		if (queryData != null) {
			StringBuffer sb = new StringBuffer(urlMain);
			StringBuffer sbParams = new StringBuffer();
			if (urlMain.indexOf('?') != -1) {
				sb.append('&');
			} else {
				sb.append('?');
			}
			ArrayList<String> alName = new ArrayList<String>();
			HashMap<String, String> maps = new HashMap<String, String>();
			for (BasicNameValuePair nv : queryData) {
				if (nv != null) {
					if (!nv.getValue().trim().equals("")) {
						alName.add(nv.getName());
						maps.put(nv.getName(), nv.getValue());
						sb.append(nv.getName()).append('=').append(URLEncoder.encode(nv.getValue())).append('&');
					}
				}
			}
			String strClientInfo = Manager.getClientInfo();
			alName.add("client_info");
			maps.put("client_info", strClientInfo);
			sb.append("client_info").append('=').append(strClientInfo).append('&');

			Collections.sort(alName);
			for (String name : alName) {
				sbParams.append(name).append('=').append(maps.get(name)).append('&');
			}
			if (sbParams.length() > 1) {
				sbParams.deleteCharAt(sbParams.length() - 1);
			}
			String needMd5Str = sbParams.toString() + appSerret;
			sb.append("sign=").append(URLEncoder.encode(StringUtil.MD5(needMd5Str).toUpperCase()));
			return methodGet(sb.toString(), useCache);
		}
		return methodGet(urlMain, useCache);
	}

	/**
	 * @param urlString
	 *            请求rul地址
	 * @param useCache
	 *            是否使用文件缓存
	 * @return
	 * @WZL wzl200711402@163.com @2012-7-27@上午10:53:29
	 * @description HttpGet 請求
	 */
	public int methodGet(String urlString, boolean useCache) {
		try {
			if (urlString.indexOf('?') == -1) {
				// urlString = urlString + "?sign=" +
				// StringUtil.MD5(Constant.MD5KEY).toUpperCase();
			}
			url = new URL(urlString);

			this.useCache = useCache;
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return NET_URL_ERROR;
		}
		return doMethod(HTTP_METHOD_GET);
	}

	/**
	 * @param urlString
	 *            请求url域名地址
	 * @param useCache
	 *            是否使用文件缓存
	 * @param postParams
	 *            请求参数
	 * @return
	 * @WZL wzl200711402@163.com @2012-7-27@上午10:52:48
	 * @description HttpPost 带参数请求
	 */
	public int methodPost(String urlString, boolean useCache, final BasicNameValuePair... postParams) {
		try {
			url = new URL(urlString);
			this.useCache = useCache;

			ArrayList<String> alName = new ArrayList<String>();
			HashMap<String, String> maps = new HashMap<String, String>();
			for (BasicNameValuePair nv : postParams) {
				if (nv != null) {
					if (!nv.getValue().trim().equals("")) {
						alName.add(nv.getName());
						maps.put(nv.getName(), nv.getValue());
						postData.add(new BasicNameValuePair(nv.getName(), URLEncoder.encode(nv.getValue())));
					}
				}
			}
			Collections.sort(alName);
			StringBuffer sbParams = new StringBuffer();
			for (String name : alName) {
				sbParams.append(name).append('=').append(maps.get(name)).append('&');
			}
			sbParams.deleteCharAt(sbParams.length() - 1);
			// String needMd5Str = sbParams.toString() + Constant.MD5KEY;
			// postData.add(new BasicNameValuePair("sign",
			// URLEncoder.encode(StringUtil.MD5(needMd5Str).toUpperCase())));
		} catch (MalformedURLException e) {
			return NET_URL_ERROR;
		}
		return doMethod(HTTP_METHOD_POST);
	}

	/**
	 * @param defaultHeaders
	 *            头部信息
	 * @WZL wzl200711402@163.com @2012-7-27@上午10:34:32
	 * @description 设置默认的头部信息
	 */
	public void setDefaultHeaders(BasicHeader... defaultHeaders) {
		this.defaultHeaders = defaultHeaders;
		if (defaultHeaders != null) {
			useDefaultHeader = true;
		} else {
			useDefaultHeader = false;
		}
	}

	/**
	 * @param useDefaultHeader
	 *            是否使用头部信息
	 * @WZL wzl200711402@163.com @2012-7-27@上午10:34:47
	 * @description 设置是否使用头部信息
	 */
	public void setUseDefaultHeader(boolean useDefaultHeader) {
		this.useDefaultHeader = useDefaultHeader;
	}

	/**
	 * @return
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-7-27@下午02:36:38
	 * @description 将http请求返回的流信息转换为text文本
	 */
	public String toText() throws IOException {
		if (useCache && Manager.useNetCache) {
			String cacheFilePath = Manager.getCACHE_DATA_DIR() + urlMd5;
			is = new FileInputStream(new File(cacheFilePath));
		}
		if (is == null) {
			return "";
		}
		if (contentEncoding == null) {
			contentEncoding = NetConstant.DEFAULT_CONTENT_CHARSET;
		}

		CharArrayBuffer buffer = new CharArrayBuffer(contentLength < 0 ? NetConstant.HTTP_READ_BUFFER : (int) contentLength);
		Reader reader = new InputStreamReader(is, contentEncoding);
		try {
			char[] tmp = new char[NetConstant.HTTP_READ_BUFFER];
			int len = -1;
			while ((len = reader.read(tmp)) != -1) {
				buffer.append(tmp, 0, len);
			}
		} finally {
			reader.close();
			is.close();
			close();
		}
		return buffer.toString();
	}

	/**
	 * @return
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-7-27@下午02:37:35
	 * @description 将http请求返回的流信息转换为字节数组
	 */
	public byte[] toByteArray() throws IOException {
		if (is == null) {
			return new byte[] {};
		}
		// 如果服务器端采用gzip压缩传输，这里会有问题
		ByteArrayBuffer buffer = new ByteArrayBuffer(contentLength < 0 ? NetConstant.HTTP_READ_BUFFER : (int) contentLength);
		try {
			byte[] tmp = new byte[NetConstant.HTTP_READ_BUFFER];
			int l;
			while ((l = is.read(tmp)) != -1) {
				buffer.append(tmp, 0, l);
			}
		} finally {
			is.close();
			close();
		}
		return buffer.toByteArray();
	}

	/**
	 * @return
	 * @throws SAXException
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-7-27@下午02:39:42
	 * @description 将http请求返回的流信息转换为Document
	 */
	public Document toDocument() throws SAXException, IOException {
		if (is == null) {
			return null;
		}
		Document document = null;
		try {
			document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (FactoryConfigurationError e) {
			e.printStackTrace();
		} finally {
			is.close();
			close();
		}
		return document;
	}

	/**
	 * @return
	 * @throws JSONException
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-7-27@下午02:40:35
	 * @description 将http请求返回的流信息转换为JSONObject
	 */
	public JSONObject toJsonObject() throws JSONException, IOException {
		String text = toText();
		if (text == null || text.equals("")) {
			return null;
		}
		JSONObject jsonObject = new JSONObject(text);
		return jsonObject;
	}

	// 保存is流到文件中，考虑内存、SD卡存储等情况
	/**
	 * @param fileName
	 * @return
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-7-27@下午03:10:56
	 * @description 将http请求返回的流信息保存在文件中
	 */
	public synchronized boolean toFile(String fileName) throws IOException {
		boolean result = false;
		File file = new File(fileName);
		if (is != null) {
			try {
				FileOutputStream fos = new FileOutputStream(file);
				byte[] temp = new byte[NetConstant.FILE_READ_BUFFER];
				int i = 0;
				while ((i = is.read(temp)) > 0) {
					fos.write(temp, 0, i);
				}
				fos.close();
				result = true;
				MLog.e("HttpClient:", fileName + " to file ok");

			} catch (IOException e) {
				MLog.e("HttpClient:", fileName + " to file false");
				e.printStackTrace();
				result = false;
			} finally {
				is.close();
				close();
			}
		}
		return result;
	}

	/**
	 * @param fileName
	 * @param inputStream
	 * @return
	 * @throws IOException
	 * @WZL wzl200711402@163.com @2012-12-6@上午09:18:16
	 * @description 将指定图片流保存到文件中
	 */
	public synchronized boolean toFile(String fileName, InputStream inputStream) throws IOException {
		boolean result = false;
		File file = new File(fileName);
		try {
			if (file.createNewFile()) {
				FileOutputStream fos = new FileOutputStream(file);
				byte[] temp = new byte[NetConstant.FILE_READ_BUFFER];
				int i = 0;
				while ((i = inputStream.read(temp)) > 0) {
					fos.write(temp, 0, i);
				}
				fos.close();
				result = true;
			} else {
				result = false;
			}

		} catch (IOException e) {
			e.printStackTrace();
			result = false;
		} finally {
			inputStream.close();
			close();
		}
		return result;
	}

	/**
	 * @return
	 * @WZL wzl200711402@163.com @2012-12-6@上午09:42:55
	 * @description 直接返回url对应的数据流
	 */
	public InputStream toInputStream(String url, boolean useCache) {
		methodGet(url, useCache);
		return is;
	}

	public String getCacheFileName() {
		return urlMd5;
	}
}
